#pragma once
/*
格式化模块:
功能:将一个日志对象格式化成一个字符串

接口设计:
1、构造时告诉格式化对象格式化字符串;
2、format接口:给这个接口传递一个日志对象，将其转换成格式化字符串

格式化接口设计:本质就是按照一定顺序从日志对象中取出对应元素来插入字符串中！
比如：%d%m%t 就是告诉我们先从日志对象中取出时间元素、再取出消息主体元素、再取出线程id元素;

要是我们能够将这个顺序保存起来，那么后面再格式化日志对象的时候，我们只需要遍历这个顺序从日志对象中取出对应元素即可！

因此格式化类的属性包含两个:
1、string :保存格式化字符串;
2、一个容器:保存日志对象的取值“顺序”！
如何得到这个顺序?
分析格式化字符串->得到一个格式化字符，创建一个子格式化对象,该子格式化对象表示从日志对象中获取特定元素;
比如:%d%m%t;
1、形成%d的子格式化对象,该格式化对象表示从日志对象中获取时间元素; A
2、形成%m的子格式化对象,该格式化对象表示从日志对象中获取主体消息元素; B
3、形成%t的子格式化对象,该格式化对象表示从日志对象中获取线程id元素; C

我们得将分析出来的ABC对象保存起来，然后后面再格式化日志对像的时候就不用再分析格式化字符串,而是直接遍历ABC三个对象按照一定顺序从日志对象中获取元素了

得用一个公共类型来接收ABC三个对象，考虑继承(利用父类指针或引用来接收这些对象)
这样顺序如何保存的问题得以解决!元素也获取到了！

但是最后我们是要的字符串啊！

因此父类提供一个虚函数，该虚函数专门从日志对象中获取指定元素，然后转换成字符串的接口！
*/
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <sstream>
#include <time.h>
#include <unordered_set>
#include "logMes.hpp"
#include "util.hpp"

namespace LoggerSpace
{
    // 格式化类
    class Formater
    {
    private:
        // 子格式化对象的公共父类，写出来是为了方便保存"顺序",同时也方便后续使用统一接口输出...
        class FormatItem
        {
        public:
            virtual ~FormatItem() {};
            // 公共接口，从日志对象中获取指定元素，并格式化成字符串
            virtual std::string format(const LoggerSpace::LogMes&) = 0;
        };

        // 开始派生...
        //%m对应的子格式化对象
        class MsgFormatItem : public FormatItem
        {
        public:
            // 从日志对象中获取主体消息
            std::string format(const LoggerSpace::LogMes& mes) override
            {
                return mes._payload;
            }
        };
        //%p对应的子格式化对象
        class LevelFormatItem : public FormatItem
        {
        public:
            // 从日志对象中获取日志等级元素,并转换为字符串
            std::string format(const LoggerSpace::LogMes& mes) override
            {
                return levelToString(mes._lev);
            }
        };
        //%c对应的子格式化对象
        class NameFormatItem : public FormatItem
        {
        public:
            // 从日志对象获取日志器名称
            std::string format(const LoggerSpace::LogMes& mes) override
            {
                return mes._loggerName;
            }
        };
        //%t对应的子格式化对象
        class ThreadFormatItem : public FormatItem
        {
        public:
            // 从日志对象获取线程id元素
            std::string format(const LoggerSpace::LogMes& mes) override
            {
                std::stringstream fstr;
                fstr << mes._tid;
                return fstr.str();
            }
        };
        //%d对应的子格式化对象
        class TimeFormatItem : public FormatItem
        {
        private:
            std::string _childFmt;

        public:
            TimeFormatItem(const std::string& Cfmt = "%H:%M:%S") : _childFmt(Cfmt) {}
            // 从日志对象获取时间元素,并按照%d字符对应的子格式转换为字符串
            // eg: %d -> %H:%M:%S
            std::string format(const LoggerSpace::LogMes& mes) override
            {
                time_t t = mes._tm;
                char buffer[1024];
                struct tm tmp;
                // 将时间戳现转换为时间结构体
               
#ifdef _WIN32
                localtime_s(&tmp, &t);
#elif __linux__
                localtime_r(&t,&tmp);
#endif 
                // 将时间结构体转换为格式化字符串
                strftime(buffer, sizeof(buffer), _childFmt.c_str(), &tmp);
                return buffer;
            }
        };
        //%f对应的子格式化对象
        class CFileFormatItem : public FormatItem
        {
        public:
            // 从mes对象中获取文件名
            std::string format(const LoggerSpace::LogMes& mes) override
            {
                return mes._fileName;
            }
        };
        //%l对应的子格式化对象
        class CLineFormatItem : public FormatItem
        {
        public:
            // 从mes对象中获取行号
            std::string format(const LoggerSpace::LogMes& mes) override
            {

                return std::to_string(mes._line);
            }
        };
        //%T对应的子格式
        class TabFormatItem : public FormatItem
        {
        public:
            // 输出tab
            std::string format(const LoggerSpace::LogMes& mes) override
            {
                return "    ";
            }
        };
        //%n对应的子格式化对象
        class NLineFormatItem : public FormatItem
        {
        public:
            // 输出换行
            std::string format(const LoggerSpace::LogMes& mes) override
            {
                return "\n";
            }
        };
        // 非格式化字对应的符子格式化对象(原样输出)
        class OtherFormatItem : public FormatItem
        {
        public:
            OtherFormatItem(const std::string& str) : _oldStr(str) {}
            // 原样输出非格式化字符
            std::string format(const LoggerSpace::LogMes& mes) override
            {
                return _oldStr;
            }
        private:
            std::string _oldStr;
        };

    private:
        // 分析格式化字符串的工作
        bool parsePattern()
        {
            char key = -1;
            std::string val;
            int pos = 0;
            while (pos < _fmt.size())
            {
                val.clear();
                while (pos < _fmt.size() && _fmt[pos] != '%')
                    val += _fmt[pos++];
                key = -1;
                _items.push_back(createItem(key, val));
                //没有遇到格式化字符
                if(pos>=_fmt.size())
                return true;
                //%后面没有字符了，错误格式化字符串
                if (pos + 1 >= _fmt.size())
                    return false;
                if (_fmt[pos + 1] == '%')
                {
                    key = -1, val = "%";
                    _items.push_back(createItem(key, val));
                    pos += 2;
                    continue;
                }
                // 检查是不是合法格式字符
                key = _fmt[pos + 1];
                auto it = _fmtSet.find(key);
                //%x这种错误格式化字符
                if (it == _fmtSet.end())
                    return false;
                if (pos + 2 >= _fmt.size() || _fmt[pos + 2] != '{')
                {
                    val = "";
                    pos += 2;
                }
                else
                {
                    int cur = pos + 3;
                    while (cur < _fmt.size() && _fmt[cur] != '}')
                        cur++;
                    // 没找到'}'
                    if (cur >= _fmt.size())
                        return false;
                    // 找到了'}'
                    val = std::string(_fmt.begin() + pos + 3, _fmt.begin() + cur);
                    pos = cur + 1;
                }
                _items.push_back(createItem(key, val));
            }
            return true;
        }
        // 根据格式化字符创建对应的格式化对象 %d{%H:%M:%S}可能会出现这种情况:key=%d,val=%H:%M:%S
        std::shared_ptr<FormatItem> createItem(const char& key, const std::string& val)
        {
            switch (key)
            {
            case 'd':
                if (val == "")
                    return std::make_shared<TimeFormatItem>();
                else
                    return std::make_shared<TimeFormatItem>(val);
            case 'T':
                return std::make_shared<TabFormatItem>();
            case 't':
                return std::make_shared<ThreadFormatItem>();
            case 'p':
                return std::make_shared<LevelFormatItem>();
            case 'c':
                return std::make_shared<NameFormatItem>();
            case 'f':
                return std::make_shared<CFileFormatItem>();
            case 'l':
                return std::make_shared<CLineFormatItem>();
            case 'm':
                return std::make_shared<MsgFormatItem>();
            case 'n':
                return std::make_shared<NLineFormatItem>();
            default:
                return std::make_shared<OtherFormatItem>(val);
            }
        }
    public:
        Formater(const std::string& fmt = "[%d{%H:%M:%S}]%T[%t]%T[%p]%T[%c]%T[%f:%l]%T%m%n") : _fmt(fmt)
        {
            // 分析格式化字符串，形成"取值顺序",方便后续格式化操作
            if (!parsePattern())
                throw std::string("格式化字符串错误!");
        }
        // 将日志对象格式化的接口
        std::string format(const LoggerSpace::LogMes& mes)
        {
            std::string ret; // 记录格式化字符串
            for (auto& e : _items)
            {
                ret += e->format(mes);
            }
            return ret;
        }
        static std::unordered_set<char> _fmtSet; // 用来记录格式化字符,所有formater类都要共享该集合，为避免每次都创建，故设为静态
    private:
        std::string _fmt;                                // 格式化字符串
        std::vector<std::shared_ptr<FormatItem>> _items; // 用来记录取值"顺序"
    };
    std::unordered_set<char> Formater::_fmtSet = { 'd', 'T', 't', 'p', 'c', 'f', 'l', 'm', 'n' };
}
